package com.nq.fund.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.nq.common.ServerResponse;
import com.nq.dao.fund.StFundKlineMapper;
import com.nq.dao.fund.StFundMapper;
import com.nq.dao.fund.StFundMovementMapper;
import com.nq.fund.FundSimulatorComponent;
import com.nq.fund.entity.StFund;
import com.nq.fund.entity.StFundKline;
import com.nq.fund.entity.StFundMovement;
import com.nq.fund.service.IStFundKlineService;
import com.nq.fund.vo.StFundKlinePo;
import com.nq.fund.vo.StFundKlineVo;
import com.nq.utils.DateTimeUtil;
import com.nq.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.text.ParseException;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Random;

/**
 * @Description: st_fund_kline
 * @Author: jeecg-boot
 * @Date:   2024-06-03
 * @Version: V1.0
 */
@Service
@Slf4j
public class StFundKlineServiceImpl extends ServiceImpl<StFundKlineMapper, StFundKline> implements IStFundKlineService {

    @Resource
    private FundSimulatorComponent simulatorComponent;
    @Resource
    private StFundMapper fundMapper;
    @Resource
    private StFundKlineMapper fundKlineMapper;
    @Resource
    private StFundMovementMapper fundMovementMapper;

    @Value("${spring.profiles.active}")
    private String active;


    @Override
    public void addBatch(String fundCode, String startTime, String endTime) {

        try {
            Date dateTimeStart = DateUtils.parseDate(startTime, "yyyy-MM-dd HH:mm:ss");
            Date dateTimeEnd = DateUtils.parseDate(endTime, "yyyy-MM-dd HH:mm:ss");
            generateDailyData(dateTimeStart, dateTimeEnd, fundCode);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }

//        for (int i = 0; i < number; i++) {
//            // 将数据的生成时间设置为当前天的开始时间戳
//            long timestamp = getStartOfDayTimestamp(i, startTime) / 1000;
//
//            execute(fundCode, timestamp);
//
//
//        }

    }

    @Override
    public StFundKline getYesterdayKline(String fundCode, long timestamp) {
        return fundKlineMapper.getYesterdayKline(fundCode, timestamp);
    }

    @Override
    public int addKline(StFundKlinePo klinePo) {

        String createTime = klinePo.getCreateTime();
        String currentPrice = klinePo.getClose();
        String fundCode = klinePo.getFundCode();

        // 时间转成时间戳
        long timestamp = DateUtils.getMillis(new Date());
        StFundKline yesterdayKline = getYesterdayKline(fundCode, timestamp);

        // 生成K线数据
        BigDecimal price = new BigDecimal(currentPrice);
        StFundKline kline = new StFundKline();
        if (null != yesterdayKline) {
            BigDecimal close = yesterdayKline.getClose();
            kline.setOpen(close);
        }else {
            kline.setOpen(price);
        }
        kline.setFundCode(fundCode);
        kline.setClose(price);
        kline.setLow(price);
        kline.setHigh(price);
        kline.setVolume(BigDecimal.ZERO);
        kline.setTurnover(BigDecimal.ZERO);
        kline.setTimestamp(timestamp);
        kline.setCreateTime(new Date());
        return fundKlineMapper.insert(kline);
    }

    @Override
    public int editKline(StFundKlinePo klinePo) {
        String createTime = klinePo.getCreateTime();
        String currentPrice = klinePo.getClose();
        String fundCode = klinePo.getFundCode();

        // 时间转成时间戳
        long timestamp = DateUtils.getMillis(new Date());
        StFundKline yesterdayKline = getYesterdayKline(fundCode, timestamp);

        // 生成K线数据
        BigDecimal price = new BigDecimal(currentPrice);
        StFundKline kline = new StFundKline();
        kline.setId(klinePo.getId());
        if (null != yesterdayKline) {
            BigDecimal close = yesterdayKline.getClose();
            kline.setOpen(close);
        }else {
            kline.setOpen(price);
        }
        kline.setFundCode(fundCode);
        kline.setClose(price);
        kline.setLow(price);
        kline.setHigh(price);
        kline.setVolume(BigDecimal.ZERO);
        kline.setTurnover(BigDecimal.ZERO);
        kline.setTimestamp(timestamp);
        kline.setCreateTime(new Date());
        return fundKlineMapper.updateById(kline);
    }

    @Override
    public void deleteKline(String fundCode) {
        fundKlineMapper.deleteByFundCode(fundCode);
    }

    private void generateDailyData(Date startDateTime, Date endDateTime, String fundCode) {

        LocalDateTime start = startDateTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        LocalDateTime end = endDateTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();

        while (!start.isAfter(end)) {
            execute(fundCode, start.atZone(ZoneId.systemDefault()).toEpochSecond());
            start = start.plusDays(1); // 假设你希望每天执行一次
        }
    }

    public void execute(String code, long timestamp){
        log.info("基金生成K线开始执行");
        // 查询出所有的基金信息，进行模拟K线
        QueryWrapper<StFund> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("fund_code", code);
        StFund stFund = fundMapper.selectOne(queryWrapper);
        if (null != stFund) {
            Integer fundId = stFund.getId();
            String fundCode = stFund.getFundCode();
            // 当前价格
            BigDecimal currentPrice = stFund.getCurrentPrice();
            // 最大波动范围
            BigDecimal maxFluctuation = stFund.getMaxFluctuation();
            // 上涨趋势
            BigDecimal upwardTrend = stFund.getUpwardTrend();

            // 模拟K线
            generatedFundMovement(fundId, fundCode, currentPrice, maxFluctuation, upwardTrend, timestamp);
        }


        log.info("基金生成K线结束执行");
    }

    // 生成每日最新基金净值数据
    public void generatedFundMovement(Integer fundId, String fundCode, BigDecimal initialPrice, BigDecimal maxFluctuation, BigDecimal upwardTrend, long timestamp){

        BigDecimal common = new BigDecimal("5");
        BigDecimal max = maxFluctuation.divide(common, 4, RoundingMode.HALF_UP);
        BigDecimal upward = upwardTrend.divide(common, 4, RoundingMode.HALF_UP);

        // 查询昨天的收盘价格  时间倒序查询最后一次的数据
        QueryWrapper<StFundKline> klineQueryWrapper = new QueryWrapper<>();
        klineQueryWrapper.eq("fund_code", fundCode);
        klineQueryWrapper.orderByDesc("timestamp");
        klineQueryWrapper.last(" limit 1");
        StFundKline stFundKline = fundKlineMapper.selectOne(klineQueryWrapper);

        BigDecimal previousPrice = new BigDecimal("0"); // 用于存储上一次的价格
        if (null != stFundKline) {
            BigDecimal close = stFundKline.getClose();
            previousPrice = close;
        }

        // 记录当前循环中的最大值
        BigDecimal high = new BigDecimal("0");
        // 记录当前循环中的最小值
        BigDecimal low = new BigDecimal("0");
        BigDecimal open = new BigDecimal("0");
        BigDecimal close = new BigDecimal("0");

        double volume = Math.round(Math.random() * 100) + 10;
        BigDecimal volumeBigDecimal = BigDecimal.valueOf(volume); // 将volume转换为BigDecimal对象

        for (int i = 0; i < 5; i++) {
            BigDecimal currentPrice = simulatePrice(initialPrice, max, upward);

            // 计算涨跌幅百分比
            BigDecimal priceChangePercentage;
            if (previousPrice.compareTo(BigDecimal.ZERO) == 0) {
                // 如果previousPrice为0，则将价格变化百分比设置为当前价格除以100
                priceChangePercentage = currentPrice.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP);
            } else {
                // 否则，执行常规的价格变化百分比计算
                priceChangePercentage = currentPrice.subtract(previousPrice)
                        .divide(previousPrice, 4, RoundingMode.HALF_UP)
                        .multiply(BigDecimal.valueOf(100));
            }

            // 计算涨跌值变化
            BigDecimal priceChange = currentPrice.subtract(previousPrice);

            StFundMovement fundMovement = fundMovementMapper.queryByCode(fundCode);
            if (null == fundMovement) {
                // 存储随机记录的值
                StFundMovement movement = new StFundMovement();
                movement.setFundCode(fundCode);
                movement.setPrice(currentPrice.setScale(2, RoundingMode.HALF_UP));
                movement.setUp(priceChangePercentage.setScale(2, RoundingMode.HALF_UP)); // 设置涨跌幅百分比
                movement.setChange(priceChange.setScale(2, RoundingMode.HALF_UP)); // 设置涨跌值变化
                movement.setCreateTime(new Date());
                fundMovementMapper.insert(movement);

            }

            previousPrice = currentPrice; // 更新上一次的价格为当前价格
            // 更新最大值和最小值
            if (currentPrice.compareTo(high) > 0) {
                high = currentPrice;
            }
            if (currentPrice.compareTo(low) < 0) {
                low = currentPrice;
            }
            // 获取开盘价
            if (i == 0) {
                open = currentPrice;
                // 第一次循环需要给low赋值
                low = currentPrice;
            }
            if (i == 4) {
                close = currentPrice;
            }

        }

        BigDecimal amount = (open.add(high).add(low).add(close)).divide(BigDecimal.valueOf(4), RoundingMode.HALF_UP).multiply(volumeBigDecimal);

        // 将Timestamp转换为Date
        Date date = new Date(timestamp * 1000);
        String formatDateTime = DateUtil.formatDateTime(date);
        // 储存K线数据  每天只能存储一条数据
        StFundKline kline = fundKlineMapper.queryCurrentKlineDataByCurrentDay(fundId, formatDateTime);
        if (null == kline) {
            StFundKline fundKline = new StFundKline();
            fundKline.setFundCode(fundCode);
            fundKline.setHigh(high.setScale(2, RoundingMode.HALF_UP));
            fundKline.setLow(low.setScale(2, RoundingMode.HALF_UP));
            fundKline.setOpen(open.setScale(2, RoundingMode.HALF_UP));
            fundKline.setClose(close.setScale(2, RoundingMode.HALF_UP));
            fundKline.setTurnover(amount.setScale(2, RoundingMode.HALF_UP));
            fundKline.setVolume(volumeBigDecimal.setScale(2, RoundingMode.HALF_UP));
            fundKline.setTimestamp(timestamp * 1000);
            fundKline.setCreateTime(date);
            fundKlineMapper.insert(fundKline);

//            // 更新该基金当前价格
//            StFund updateFund = new StFund();
//            BigDecimal currentPrice = close.setScale(2, RoundingMode.HALF_UP);
//            updateFund.setCurrentPrice(currentPrice);
//            UpdateWrapper<StFund> updateWrapper = new UpdateWrapper<>();
//            updateWrapper.eq("fund_code", fundCode);
//            fundMapper.update(updateFund, updateWrapper);

        }

    }

    public BigDecimal simulatePrice(BigDecimal initialPrice, BigDecimal maxFluctuation, BigDecimal upwardTrend) {
        Random random = new Random();

        // 生成随机波动
        BigDecimal fluctuation = maxFluctuation.multiply(BigDecimal.valueOf(random.nextDouble() * 2 - 1));

        // 缓慢上涨的趋势
        BigDecimal slowTrend = upwardTrend;

        // 计算下一个价格
        BigDecimal nextPrice = initialPrice.add(fluctuation).add(slowTrend);

        // 确保价格不会小于等于 0
        if (nextPrice.compareTo(BigDecimal.ZERO) > 0) {
            initialPrice = nextPrice;
        }

        System.out.println("当前基金价格：" + initialPrice);
        return initialPrice;
    }

    private static long getStartOfDayTimestamp(int daysBeforeNow, String startTime) {
        // 获取当前时间
        long currentTime = System.currentTimeMillis();
        if (StringUtils.isNotBlank(startTime)) {
            try {
                Date date = DateUtils.parseDate(startTime, "yyyy-MM-dd HH:mm:ss");
                currentTime = DateUtils.getMillis(date);
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
        // 计算指定天数之前的开始时间戳
        long startOfDayTimestamp = currentTime - (currentTime % (24 * 60 * 60 * 1000)) - (daysBeforeNow * 24 * 60 * 60 * 1000);
        return startOfDayTimestamp;
    }
}
